#include "resource_manager.h"

#include "resource_loader.h"
#include "resource_scanner.h"
#include <string>
#include <sstream>
using namespace std;

ResourceManager::
ResourceManager () {
  initLoadersMap();
}

ResourceManager::
~ResourceManager ()  {
  // Unload each resource using the appropriate loaders.
  unloadAll();
  // Kill each loader.
  ImageLoader::kill();
}

void ResourceManager::
initLoadersMap () {
  ImageLoader* imgLoader = ImageLoader::getInstance();
  ext2loader["jpg"] = imgLoader;
  ext2loader["png"] = imgLoader;
}

void ResourceManager::
initialize (const string& rootpath, bool loadAtStart) {
  /**
    * First of all visit the rootpath recursively and build an index
    * of all the available resources (made by a Lua script).
    */
  ResourceScanner::scan(resourceIndex, rootpath);
  
  /**
    * If requested, load all the available resources now.
    */
  if (loadAtStart) {
    loadAll();
  }
}

void* ResourceManager::
load (const string& key) {
  stringstream ss;

  // First, verify that there isn't already a resource loaded.
  map<string,void*>::iterator itrFindData = resources.find(key);
  // There is a resource already loaded.
  if (itrFindData != resources.end()) {
    ss << "Unloading previously loaded data, replaced by : \"" << key << "\"";
    LOG_WARNING(ss.str());
    ss.str().clear();
    unload(key);
  }

  // Verify that there is a resource index, else do nothing.
  map<string,string>::iterator itrFindIndex = resourceIndex.find(key);
  // No index.
  if (itrFindIndex == resourceIndex.end()) {
    ss << "No index for resource : \"" << key << "\"" << endl;
    LOG_ERROR(ss.str());
    return NULL;
  }
  // Get the full extension from the full path.
  string ext = getExtension((*itrFindIndex).second);

  // Verify that there is a loader for that extension.
  map<string, ResourceLoader*>::iterator itrFindLoader = ext2loader.find(ext);
  if (itrFindLoader == ext2loader.end()) {
    ss << "No loader for extension : \"" << ext << "\"";
    ss << ", resource : \"" << key << "\" not loaded.";
    LOG_ERROR(ss.str());
    return NULL;
  }
  // Get the loader.
  ResourceLoader* loader = (*itrFindLoader).second;

  // Load the resource using the full path.
  void* resource = loader->load((*itrFindIndex).second);
  
  // If something went wrong, return.
  if (resource == NULL) {
    ss << "Loading of resource : \"" << key << "\" failed.";
    LOG_ERROR(ss.str());
    return NULL;
  }

  // Register the new resource in the map.
  resources.insert(pair<string,void*>(key,resource));
  
  ss << "Loaded : \"" << key << "\" (" << resource << ")";
  LOG(ss.str());
  return resource;
}

void ResourceManager::
unload (const string& key) {
  stringstream ss;

  // First, verify that the resource to free exist.
  map<string,void*>::iterator itrFindData = resources.find(key);
  // Nothing to free.
  if (itrFindData == resources.end()) {
    ss << "No data to unload : \"" << key << "\"" << endl;
    LOG_WARNING(ss.str());
    return;
  }
  // Get the resource to free.
  void* resource = (*itrFindData).second;

  // Verify that there is a resource index, else do nothing.
  map<string,string>::iterator itrFindIndex = resourceIndex.find(key);
  // No index.
  if (itrFindIndex == resourceIndex.end()) {
    ss << "No index for resource : \"" << key << "\"" << endl;
    LOG_ERROR(ss.str());
    return;
  }
  // Get the full extension from the full path.
  string ext = getExtension((*itrFindIndex).second);

  // Verify that there is a loader for that extension.
  map<string, ResourceLoader*>::iterator itrFindLoader = ext2loader.find(ext);
  if (itrFindLoader == ext2loader.end()) {
    ss << "No loader for extension : " << ext;
    ss << ", resource : \"" << key << "\" not loaded.";
    LOG_ERROR(ss.str());
    return;
  }
  // Get the loader.
  ResourceLoader* loader = (*itrFindLoader).second;

  // Unload the resource.
  loader->unload(resource);

  // Unregister the resource.
  resources.erase(key);

  ss << "Unloaded : \"" << key << "\" (" << resource << ")";
  LOG(ss.str());
}

#ifdef TEST_RESOURCE_MANAGER
#include "bnb_controller.h"
#define rootpath "/Volumes/MacAPPS/Development/brick-and-blast/data"

int main (int argc, char** argv) {
  BNBController* bnbCtrl = BNBController::getInstance();
  bnbCtrl->initialize();

  ResourceManager* resourceMgr = ResourceManager::getInstance();
  resourceMgr->initialize(rootpath);
  resourceMgr->kill();

  resourceMgr = ResourceManager::getInstance();
  resourceMgr->initialize(rootpath, false);
  
  resourceMgr->get("ball");
  resourceMgr->get("ball");
  resourceMgr->unload("ball");
  resourceMgr->get("bat");
  
  resourceMgr->kill();

  bnbCtrl->terminate();
  return EXIT_SUCCESS;
}
#endif

